﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using TetrisClient.TetrisService;
using System.Threading;
using System.ComponentModel;

namespace TetrisClient
{
    public partial class MainPage : UserControl
    {
        private TetrisWebServiceSoapClient proxy;
        private UpdateScreenThread updateScreenThread;
        private NextTetrominoThread nextTrominoThread;
        private GridManipulator tetrisUtils;

        public MainPage()
        {
            InitializeComponent();

            tetrisUtils = new GridManipulator(tetrisBoard);
            proxy = new TetrisWebServiceSoapClient();
            nextTrominoThread = new NextTetrominoThread(nextTetrominoBoard);
            updateScreenThread = new UpdateScreenThread(10000, this);

            configureEventKeys();
            configureAsyncReturnMethods();

            initializeGame();
        }

        void button_startNewGame(object sender, RoutedEventArgs e)
        {
            initializeGame();
        }

        private void initializeGame()
        {
            stackPanel.Visibility = System.Windows.Visibility.Visible;
            stackPanelGameOver.Visibility = System.Windows.Visibility.Collapsed;
            pauseLabel.Visibility = System.Windows.Visibility.Collapsed;
            proxy.GetGameConfigurationAsync();
        }

        private void configureAsyncReturnMethods()
        {
            proxy.MoveLeftCompleted += new EventHandler<MoveLeftCompletedEventArgs>(proxy_MoveLeftCompleted);
            proxy.MoveRightCompleted += new EventHandler<MoveRightCompletedEventArgs>(proxy_MoveRightCompleted);
            proxy.RotateCompleted += new EventHandler<RotateCompletedEventArgs>(proxy_RotateCompleted);
            proxy.GetBoardCompleted += new EventHandler<GetBoardCompletedEventArgs>(proxy_GetBoardCompleted);
            proxy.MoveDownCompleted += new EventHandler<MoveDownCompletedEventArgs>(proxy_MoveDownCompleted);
            proxy.GetGameConfigurationCompleted += new EventHandler<GetGameConfigurationCompletedEventArgs>(proxy_GetGameConfigurationCompleted);
            proxy.StatusCompleted += new EventHandler<StatusCompletedEventArgs>(proxy_StatusCompleted);
            proxy.GetNextTetrominoCompleted +=new EventHandler<GetNextTetrominoCompletedEventArgs>(proxy_GetNextTetrominoCompleted);
        }

        private int[][] toIntMatrix(ArrayOfInt[] matrix)
        {
            int[][] newMatrix = new int[matrix.Length][];

            for (int i = 0; i < matrix.Length; i++)
            {
                newMatrix[i] = matrix[i].ToArray();
            }
            return newMatrix;
        }

        private void updateScreen(GameStatus gameStatus)
        {
            updateScreenThread.updateRefreshTime(gameStatus.refreshTime);
            if (nextTrominoThread.setNextTetromino(gameStatus.nextTetromino))
            {
                proxy.GetNextTetrominoAsync();
            }
            tetrisUtils.updateGameBoard(toIntMatrix(gameStatus.board.ToArray<ArrayOfInt>()));
            updateGameInformation(gameStatus);
        }

        void proxy_StatusCompleted(Object sender, StatusCompletedEventArgs args)
        {
            updateGameInformation(args.Result as GameStatus);
        }

        void proxy_GetNextTetrominoCompleted(Object sender, GetNextTetrominoCompletedEventArgs args)
        {
            nextTrominoThread.updateNextTetromino(toIntMatrix(args.Result.ToArray()));
        }

        void proxy_GetGameConfigurationCompleted(Object sender, GetGameConfigurationCompletedEventArgs args)
        {
            GameConfiguration gameConfiguration = args.Result as GameConfiguration;

            updateScreenThread = new UpdateScreenThread(gameConfiguration.refreshTime, this);
            nextTrominoThread = new NextTetrominoThread(nextTetrominoBoard);
            tetrisUtils.createGameBoard(gameConfiguration.numColumns, gameConfiguration.numRows);
            updateScreenThread.startThread();

            nextTrominoThread.createNextTetrominoBoard(gameConfiguration.numColumnsNextTetromino, gameConfiguration.numRowsNextTetromino);
            nextTrominoThread.startThread();
        }

        void proxy_GetBoardCompleted(Object sender, GetBoardCompletedEventArgs args)
        {
            // REMOVEME
        }

        private void proxy_RotateCompleted(Object sender, RotateCompletedEventArgs args)
        {
            updateScreen(args.Result as GameStatus);
        }

        private void proxy_MoveRightCompleted(Object sender, MoveRightCompletedEventArgs args)
        {
            updateScreen(args.Result as GameStatus);
        }

        private void proxy_MoveLeftCompleted(Object sender, MoveLeftCompletedEventArgs args)
        {
            updateScreen(args.Result as GameStatus);
        }

        private void proxy_MoveDownCompleted(Object sender, MoveDownCompletedEventArgs args)
        {
            updateScreen(args.Result as GameStatus);
        }

        private void updateGameInformation(GameStatus gameStatus)
        {
            contentScoreLabel.Content = gameStatus.score;
            contentLevelLabel.Content = gameStatus.level;
            contentLinesLabel.Content = gameStatus.refreshTime; // CHANGEME
            checkGameOver(gameStatus);
        }

        private void configureEventKeys()
        {
            userControlTetris.KeyDown += new KeyEventHandler(tetrisBoard_KeyDown);
        }

        public void moveDown()
        {
            proxy.MoveDownAsync();
        }

        private void tetrisBoard_KeyDown(object sender, KeyEventArgs e)
        {
            if (Monitor.TryEnter(this, 0))
            {
                try
                {
                    keyEvent(e.Key);
                }
                finally
                {
                    Monitor.Exit(this);
                }
            }
        }

        private void checkGameOver(GameStatus gameStatus)
        {
            if (gameStatus.isGameOver)
            {
                updateScreenThread.stopThread();
                nextTrominoThread.stopThread();
                stackPanel.Visibility = System.Windows.Visibility.Collapsed;
                stackPanelGameOver.Visibility = System.Windows.Visibility.Visible;
                myScoreLabel.Content = gameStatus.score;
            }
        }

        private void releasePause()
        {
            stackPanel.Visibility = System.Windows.Visibility.Visible;
            pauseLabel.Visibility = System.Windows.Visibility.Collapsed;
            updateScreenThread.releasePause();
        }

        private void pause()
        {
            updateScreenThread.pause();
            stackPanel.Visibility = System.Windows.Visibility.Collapsed;
            pauseLabel.Visibility = System.Windows.Visibility.Visible;
        }

        private void keyEvent(Key key)
        {
            switch (key)
            {
                case Key.Left:
                    if (!updateScreenThread.isPaused())
                    {
                        proxy.MoveLeftAsync();
                    }
                    break;
                case Key.Right:
                    if (!updateScreenThread.isPaused())
                    {
                        proxy.MoveRightAsync();
                    }
                    break;
                case Key.Down:
                    if (!updateScreenThread.isPaused())
                    {
                        proxy.MoveDownAsync();
                    }
                    break;
                case Key.Up:
                    if (!updateScreenThread.isPaused())
                    {
                        proxy.RotateAsync();
                    }
                    break;
                case Key.P:
                case Key.Space:
                    if (updateScreenThread.isPaused())
                    {
                        releasePause();
                    }
                    else
                    {
                        pause();
                    }
                    break;
                default:
                    break;
            }
        }
    }
}
